"""
文章结构分析器
分析文章的组织结构，识别开头、主体、结尾等部分
"""

import re
from typing import Dict, List, Tuple, Optional, Any
from dataclasses import dataclass
from enum import Enum
import logging

logger = logging.getLogger(__name__)


class StructurePart(Enum):
    """文章结构部分枚举"""
    TITLE = "title"
    INTRODUCTION = "introduction"
    BODY = "body"
    CONCLUSION = "conclusion"
    UNKNOWN = "unknown"


@dataclass
class StructureSegment:
    """文章段落片段"""
    part_type: StructurePart
    content: str
    start_index: int
    end_index: int
    paragraph_count: int
    word_count: int
    confidence: float


@dataclass
class StructureAnalysisResult:
    """结构分析结果"""
    segments: List[StructureSegment]
    structure_score: float
    completeness_score: float
    proportion_score: float
    logic_score: float
    problems: List[str]
    suggestions: List[Dict[str, Any]]


class StructureAnalyzer:
    """文章结构分析器"""
    
    def __init__(self):
        """初始化分析器"""
        # 理想的文章结构比例
        self.ideal_proportions = {
            StructurePart.TITLE: 0.02,
            StructurePart.INTRODUCTION: 0.15,
            StructurePart.BODY: 0.70,
            StructurePart.CONCLUSION: 0.13
        }
        
        # 开头标识词
        self.intro_keywords = [
            r"^引言", r"^前言", r"^导语", r"^概述", r"^背景",
            r"^摘要", r"^简介", r"^序言", r"^导读",
            r"随着.{0,10}的发展", r"近年来", r"当前", r"如今",
            r"在.{0,10}背景下", r"本文将", r"本文旨在"
        ]
        
        # 结尾标识词
        self.conclusion_keywords = [
            r"^结论", r"^总结", r"^结语", r"^小结", r"^展望",
            r"^后记", r"^结束语", r"综上所述", r"总的来说",
            r"总之", r"最后", r"总而言之", r"由此可见",
            r"未来.{0,10}将", r"展望未来"
        ]
        
        # 主体段落标识
        self.body_keywords = [
            r"^第[一二三四五六七八九十\d]+", r"^\d+\.",
            r"^首先", r"^其次", r"^然后", r"^接着", r"^此外",
            r"^另外", r"^同时", r"^而且", r"^并且",
            r"^一方面", r"^另一方面", r"^不仅", r"^而且"
        ]
    
    def analyze(self, content: str) -> StructureAnalysisResult:
        """
        分析文章结构
        
        Args:
            content: 文章内容
            
        Returns:
            结构分析结果
        """
        if not content or not content.strip():
            return self._empty_result("文章内容为空")
        
        # 分段处理
        segments = self._identify_segments(content)
        
        # 计算各项评分
        completeness_score = self._calculate_completeness_score(segments)
        proportion_score = self._calculate_proportion_score(segments)
        logic_score = self._calculate_logic_score(segments)
        
        # 综合评分
        structure_score = (
            completeness_score * 0.3 +
            proportion_score * 0.3 +
            logic_score * 0.4
        )
        
        # 检测问题
        problems = self._detect_problems(segments, completeness_score, proportion_score)
        
        # 生成建议
        suggestions = self._generate_suggestions(segments, problems)
        
        return StructureAnalysisResult(
            segments=segments,
            structure_score=structure_score,
            completeness_score=completeness_score,
            proportion_score=proportion_score,
            logic_score=logic_score,
            problems=problems,
            suggestions=suggestions
        )
    
    def _identify_segments(self, content: str) -> List[StructureSegment]:
        """识别文章各个部分"""
        segments = []
        paragraphs = content.split('\n\n')
        
        # 清理空段落
        paragraphs = [p.strip() for p in paragraphs if p.strip()]
        
        if not paragraphs:
            return segments
        
        current_index = 0
        
        # 识别标题（第一段如果较短）
        if len(paragraphs[0]) < 100 and not any(char in paragraphs[0] for char in '。！？'):
            segments.append(self._create_segment(
                StructurePart.TITLE,
                paragraphs[0],
                current_index,
                1.0
            ))
            current_index += len(paragraphs[0]) + 2
            paragraphs = paragraphs[1:]
        
        # 识别开头部分
        intro_end_idx = self._find_introduction_end(paragraphs)
        if intro_end_idx > 0:
            intro_content = '\n\n'.join(paragraphs[:intro_end_idx])
            segments.append(self._create_segment(
                StructurePart.INTRODUCTION,
                intro_content,
                current_index,
                0.8
            ))
            current_index += len(intro_content) + 2
            paragraphs = paragraphs[intro_end_idx:]
        
        # 识别结尾部分
        conclusion_start_idx = self._find_conclusion_start(paragraphs)
        
        # 处理主体部分
        if conclusion_start_idx > 0:
            body_paragraphs = paragraphs[:conclusion_start_idx]
            conclusion_paragraphs = paragraphs[conclusion_start_idx:]
        else:
            # 如果没有明显结尾，最后15%作为结尾
            split_point = max(1, int(len(paragraphs) * 0.85))
            body_paragraphs = paragraphs[:split_point]
            conclusion_paragraphs = paragraphs[split_point:]
        
        if body_paragraphs:
            body_content = '\n\n'.join(body_paragraphs)
            segments.append(self._create_segment(
                StructurePart.BODY,
                body_content,
                current_index,
                0.9
            ))
            current_index += len(body_content) + 2
        
        if conclusion_paragraphs:
            conclusion_content = '\n\n'.join(conclusion_paragraphs)
            segments.append(self._create_segment(
                StructurePart.CONCLUSION,
                conclusion_content,
                current_index,
                0.7
            ))
        
        return segments
    
    def _find_introduction_end(self, paragraphs: List[str]) -> int:
        """找到引言结束位置"""
        if not paragraphs:
            return 0
        
        # 检查前3段是否包含引言标识
        for i, para in enumerate(paragraphs[:3]):
            if any(re.search(pattern, para) for pattern in self.intro_keywords):
                return i + 1
        
        # 检查是否有明显的主体开始标识
        for i, para in enumerate(paragraphs[:5]):
            if any(re.search(pattern, para) for pattern in self.body_keywords):
                return i
        
        # 默认第一段为引言
        return 1 if len(paragraphs) > 1 else 0
    
    def _find_conclusion_start(self, paragraphs: List[str]) -> int:
        """找到结论开始位置"""
        if not paragraphs:
            return -1
        
        # 从后往前查找结论标识
        for i in range(len(paragraphs) - 1, max(len(paragraphs) - 4, -1), -1):
            if any(re.search(pattern, paragraphs[i]) for pattern in self.conclusion_keywords):
                return i
        
        return -1
    
    def _create_segment(self, part_type: StructurePart, content: str, 
                       start_index: int, confidence: float) -> StructureSegment:
        """创建段落片段"""
        paragraphs = content.split('\n\n')
        paragraph_count = len([p for p in paragraphs if p.strip()])
        
        # 计算字数（中英文混合）
        chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', content))
        english_words = len(re.findall(r'\b[a-zA-Z]+\b', content))
        word_count = chinese_chars + english_words
        
        return StructureSegment(
            part_type=part_type,
            content=content,
            start_index=start_index,
            end_index=start_index + len(content),
            paragraph_count=paragraph_count,
            word_count=word_count,
            confidence=confidence
        )
    
    def _calculate_completeness_score(self, segments: List[StructureSegment]) -> float:
        """计算完整性评分"""
        if not segments:
            return 0.0
        
        required_parts = {StructurePart.INTRODUCTION, StructurePart.BODY, StructurePart.CONCLUSION}
        present_parts = {seg.part_type for seg in segments}
        
        # 基础分：必要部分的存在性
        base_score = len(present_parts & required_parts) / len(required_parts)
        
        # 加分项：有标题
        if StructurePart.TITLE in present_parts:
            base_score = min(1.0, base_score + 0.1)
        
        return base_score
    
    def _calculate_proportion_score(self, segments: List[StructureSegment]) -> float:
        """计算比例评分"""
        if not segments:
            return 0.0
        
        total_words = sum(seg.word_count for seg in segments)
        if total_words == 0:
            return 0.0
        
        scores = []
        for seg in segments:
            proportion = seg.word_count / total_words
            ideal = self.ideal_proportions.get(seg.part_type, 0.1)
            
            # 计算偏离度
            deviation = abs(proportion - ideal) / ideal if ideal > 0 else 0
            score = max(0, 1 - deviation)
            scores.append(score)
        
        return sum(scores) / len(scores) if scores else 0.0
    
    def _calculate_logic_score(self, segments: List[StructureSegment]) -> float:
        """计算逻辑性评分"""
        if not segments:
            return 0.0
        
        score = 1.0
        
        # 检查顺序是否合理
        expected_order = [StructurePart.TITLE, StructurePart.INTRODUCTION, 
                         StructurePart.BODY, StructurePart.CONCLUSION]
        
        segment_types = [seg.part_type for seg in segments]
        
        # 移除不在期望顺序中的类型
        filtered_types = [t for t in segment_types if t in expected_order]
        
        # 检查顺序
        last_index = -1
        for seg_type in filtered_types:
            current_index = expected_order.index(seg_type)
            if current_index < last_index:
                score -= 0.2  # 顺序错误扣分
            last_index = current_index
        
        # 检查是否有重复部分
        if len(filtered_types) != len(set(filtered_types)):
            score -= 0.3
        
        return max(0.0, score)
    
    def _detect_problems(self, segments: List[StructureSegment], 
                        completeness_score: float, proportion_score: float) -> List[str]:
        """检测结构问题"""
        problems = []
        
        # 完整性问题
        segment_types = {seg.part_type for seg in segments}
        
        if StructurePart.INTRODUCTION not in segment_types:
            problems.append("缺少引言/开头部分")
        
        if StructurePart.BODY not in segment_types:
            problems.append("缺少主体内容")
        
        if StructurePart.CONCLUSION not in segment_types:
            problems.append("缺少结论/总结部分")
        
        # 比例问题
        if proportion_score < 0.5:
            total_words = sum(seg.word_count for seg in segments)
            for seg in segments:
                proportion = seg.word_count / total_words if total_words > 0 else 0
                ideal = self.ideal_proportions.get(seg.part_type, 0.1)
                
                if proportion > ideal * 1.5:
                    problems.append(f"{self._get_part_name(seg.part_type)}部分过长（占比{proportion:.1%}）")
                elif proportion < ideal * 0.5:
                    problems.append(f"{self._get_part_name(seg.part_type)}部分过短（占比{proportion:.1%}）")
        
        return problems
    
    def _generate_suggestions(self, segments: List[StructureSegment], 
                            problems: List[str]) -> List[Dict[str, Any]]:
        """生成优化建议"""
        suggestions = []
        
        segment_types = {seg.part_type for seg in segments}
        
        # 基于缺失部分的建议
        if StructurePart.INTRODUCTION not in segment_types:
            suggestions.append({
                "type": "structure",
                "priority": "high",
                "description": "添加引言部分",
                "detail": "建议在文章开头添加引言，介绍背景、目的和文章结构",
                "example": "随着[技术/领域]的发展，[问题/现象]日益重要。本文将从[角度1]、[角度2]等方面，深入探讨[主题]。",
                "impact": 0.2
            })
        
        if StructurePart.CONCLUSION not in segment_types:
            suggestions.append({
                "type": "structure",
                "priority": "high",
                "description": "添加结论部分",
                "detail": "建议在文章结尾添加总结，回顾要点并提出展望",
                "example": "综上所述，[总结要点1]、[要点2]。未来，随着[发展趋势]，我们期待[展望]。",
                "impact": 0.15
            })
        
        # 基于比例的建议
        total_words = sum(seg.word_count for seg in segments)
        for seg in segments:
            proportion = seg.word_count / total_words if total_words > 0 else 0
            ideal = self.ideal_proportions.get(seg.part_type, 0.1)
            
            if proportion > ideal * 1.5:
                suggestions.append({
                    "type": "structure",
                    "priority": "medium",
                    "description": f"精简{self._get_part_name(seg.part_type)}",
                    "detail": f"当前占比{proportion:.1%}，建议精简到{ideal:.1%}左右",
                    "impact": 0.1
                })
            elif proportion < ideal * 0.5:
                suggestions.append({
                    "type": "structure",
                    "priority": "medium",
                    "description": f"扩充{self._get_part_name(seg.part_type)}",
                    "detail": f"当前占比{proportion:.1%}，建议扩充到{ideal:.1%}左右",
                    "impact": 0.1
                })
        
        # 结构优化建议
        if len(segments) > 0:
            body_segments = [s for s in segments if s.part_type == StructurePart.BODY]
            if body_segments and body_segments[0].paragraph_count < 3:
                suggestions.append({
                    "type": "structure",
                    "priority": "low",
                    "description": "增加主体段落层次",
                    "detail": "建议将主体内容分成多个段落，每个段落聚焦一个观点",
                    "impact": 0.05
                })
        
        return suggestions
    
    def _get_part_name(self, part_type: StructurePart) -> str:
        """获取部分的中文名称"""
        names = {
            StructurePart.TITLE: "标题",
            StructurePart.INTRODUCTION: "引言",
            StructurePart.BODY: "主体",
            StructurePart.CONCLUSION: "结论",
            StructurePart.UNKNOWN: "未知"
        }
        return names.get(part_type, "未知")
    
    def _empty_result(self, reason: str) -> StructureAnalysisResult:
        """返回空结果"""
        return StructureAnalysisResult(
            segments=[],
            structure_score=0.0,
            completeness_score=0.0,
            proportion_score=0.0,
            logic_score=0.0,
            problems=[reason],
            suggestions=[]
        )